load("@rules_license//rules:license.bzl", "license")
load("//:bazel/bison.bzl", "genyacc")
load("//:bazel/flex.bzl", "genlex")

package(
    default_applicable_licenses = ["//:license"],
    default_visibility = ["//visibility:public"],
)

license(
    name = "license",
    package_name = "com_github_p4lang_p4c",
    license_kinds = ["@rules_license//licenses/spdx:Apache-2.0"],
    license_text = "LICENSE",
)

exports_files(["LICENSE"])

filegroup(
    name = "p4include",
    srcs = glob(["p4include/**/*.p4"]),
)

genrule(
    name = "sed_config_h",
    srcs = ["cmake/config.h.cmake"],
    outs = ["config.h"],
    # TODO: We should actually check these properly instead of just #undefing them.
    # Maybe select() can help here?
    # CONFIG_PKGDATADIR is where p4c should look for p4include at runtime.
    # This will work only if the binary is executed by Bazel. For a general
    # solution, we would need to make p4c aware of Bazel, specifically:
    # https://github.com/bazelbuild/bazel/blob/master/tools/cpp/runfiles/runfiles_src.h
    cmd = "sed -e 's|cmakedefine|define|g' \
             -e 's|define HAVE_LIBGC 1|undef HAVE_LIBGC|g' \
             -e 's|define HAVE_LIBBACKTRACE 1|undef HAVE_LIBBACKTRACE|g' \
             -e 's|define HAVE_MM_MALLOC_H 1|undef HAVE_MM_MALLOC_H|g' \
             -e 's|@MAX_LOGGING_LEVEL@|10|g' \
             -e 's|@CONFIG_PKGDATADIR@|external/%s|g' \
             < $(SRCS) > $(OUTS)" % repository_name(),
    visibility = ["//visibility:private"],
)

cc_library(
    name = "config_h",
    hdrs = ["config.h"],
    include_prefix = ".",
    deps = [":sed_config_h"],
)

cc_library(
    name = "lib",
    srcs = glob(["lib/*.cpp"]),
    hdrs = glob(["lib/*.h"]),
    deps = [
        ":config_h",
        "@boost//:format",
        "@boost//:multiprecision",
        "@com_google_absl//absl/container:flat_hash_map",
        "@com_google_absl//absl/container:node_hash_set",
        "@com_google_absl//absl/numeric:bits",
        "@com_google_absl//absl/strings",
        "@com_google_absl//absl/strings:cord",
        "@com_google_absl//absl/strings:str_format",
        "@com_google_googletest//:gtest",
    ],
)

genyacc(
    name = "p4_parser_yacc",
    src = "frontends/parsers/p4/p4parser.ypp",
    extra_outs = ["frontends/parsers/p4/stack.hh"],
    header_out = "frontends/parsers/p4/p4parser.hpp",
    source_out = "frontends/parsers/p4/p4parser.cc",
    visibility = ["//visibility:private"],
)

genyacc(
    name = "v1_parser_yacc",
    src = "frontends/parsers/v1/v1parser.ypp",
    extra_outs = ["frontends/parsers/v1/stack.hh"],
    header_out = "frontends/parsers/v1/v1parser.hpp",
    source_out = "frontends/parsers/v1/v1parser.cc",
    visibility = ["//visibility:private"],
)

genrule(
    name = "p4lexer_lex",
    srcs = ["frontends/parsers/p4/p4lexer.ll"],
    outs = ["frontends/parsers/p4/p4lexer.lex"],
    cmd = "sed '/%option outfile=\"lex.yy.c\"/d' $(SRCS) > $(OUTS)",
    visibility = ["//visibility:private"],
)

genlex(
    name = "p4lexer",
    src = "frontends/parsers/p4/p4lexer.lex",
    out = "frontends/parsers/p4/p4lexer.cc",
    prefix = "yy",
    visibility = ["//visibility:private"],
)

genrule(
    name = "v1lexer_lex",
    srcs = ["frontends/parsers/v1/v1lexer.ll"],
    outs = ["frontends/parsers/v1/v1lexer.lex"],
    cmd = "sed '/%option outfile=\"lex.yy.c\"/d' $(SRCS) > $(OUTS)",
    visibility = ["//visibility:private"],
)

genlex(
    name = "v1lexer",
    src = "frontends/parsers/v1/v1lexer.lex",
    out = "frontends/parsers/v1/v1lexer.cc",
    prefix = "yy",
    visibility = ["//visibility:private"],
)

# The ir-generator tool uses a parser built by these genlex and genyacc rules.
genlex(
    name = "ir_generator_lex",
    src = "tools/ir-generator/ir-generator-lex.l",
    out = "tools/ir-generator/ir-generator-lex.c",
    prefix = "yy",
    visibility = ["//visibility:private"],
)

genyacc(
    name = "ir_generator_yacc",
    src = "tools/ir-generator/ir-generator.ypp",
    header_out = "tools/ir-generator/ir-generator-yacc.hh",
    source_out = "tools/ir-generator/ir-generator-yacc.cc",
    visibility = ["//visibility:private"],
)

# This cc_library contains the ir-generator tool sources, including the
# ir-generator parser source from the lex/yacc output. The srcs attribute
# excludes generator.cpp since it is part of the cc_binary.
cc_library(
    name = "ir_generator_lib",
    srcs = glob(
        include = ["tools/ir-generator/*.cpp"],
        exclude = ["tools/ir-generator/generator.cpp"],
    ) + [
        "tools/ir-generator/ir-generator-yacc.cc",
    ],
    hdrs = glob(["tools/ir-generator/*.h"]),
    textual_hdrs = [
        ":ir_generator_lex",
        ":ir_generator_yacc",
    ],
    visibility = ["//visibility:private"],
    deps = [
        ":lib",
    ],
)

# The next rule builds the ir-generator tool binary.
cc_binary(
    name = "ir_generator",
    srcs = ["tools/ir-generator/generator.cpp"],
    deps = [
        ":ir_generator_lib",
        ":lib",
    ],
)

filegroup(
    name = "ir_extra_defs",
    srcs = [
        "frontends/p4-14/ir-v1.def",
        "backends/bmv2/bmv2.def",
        "backends/dpdk/dpdk.def",
        "//backends/p4tools:ir_extension",
        # p4c extensions may set this target to a `filegroup` containing
        # additional .def files.
        "@com_github_p4lang_p4c_extension//:ir_extension",
    ],
)

genrule(
    name = "ir_generated_files",
    srcs = glob(["ir/*.def"]) + [":ir_extra_defs"],
    outs = [
        "ir/gen-tree-macro.h",
        "ir/ir-generated.cpp",
        "ir/ir-generated.h",
    ],
    cmd = """
        $(location :ir_generator) \
            -t $(RULEDIR)/ir/gen-tree-macro.h \
            -i $(RULEDIR)/ir/ir-generated.cpp \
            -o $(RULEDIR)/ir/ir-generated.h \
            $(location ir/base.def) \
            $(location ir/type.def) \
            $(location ir/expression.def) \
            $(location ir/ir.def) \
            $(locations :ir_extra_defs)
    """,
    tools = [":ir_generator"],
    visibility = ["//visibility:private"],
)

# It would be better to build these modules separately, but they have cyclic
# dependencies.
cc_library(
    name = "ir_frontend_midend_control_plane",
    srcs = glob([
        "ir/**/*.cpp",
        "frontends/**/*.cpp",
        "midend/**/*.cpp",
        "control-plane/**/*.cpp",
    ]) + [
        "backends/dpdk/dbprint-dpdk.cpp",
        "backends/dpdk/printUtils.cpp",
        "backends/dpdk/spec.cpp",
        "frontends/parsers/p4/p4lexer.cc",
        "frontends/parsers/p4/p4parser.cc",
        "frontends/parsers/v1/v1lexer.cc",
        "frontends/parsers/v1/v1parser.cc",
        "ir/ir-generated.cpp",
    ],
    textual_hdrs = glob([
        "ir/**/*.h",
        "frontends/**/*.h",
        "frontends/**/*.hpp",
        "midend/**/*.h",
        "control-plane/**/*.h",
        "backends/**/*.h",
    ]) + [
        ":p4_parser_yacc",
        ":v1_parser_yacc",
        ":p4lexer",
        ":v1lexer",
        ":ir_generated_files",
    ],
    deps = [
        ":config_h",
        ":lib",
        ":p4info_dpdk_cc_proto",
        "@boost//:algorithm",
        "@boost//:functional",
        "@boost//:iostreams",
        "@com_github_p4lang_p4runtime//:p4info_cc_proto",
        "@com_github_p4lang_p4runtime//:p4runtime_cc_proto",
        "@com_github_p4lang_p4runtime//:p4types_cc_proto",
        "@com_google_absl//absl/container:btree",
        "@com_google_absl//absl/container:flat_hash_map",
        "@com_google_absl//absl/container:flat_hash_set",
        "@com_google_absl//absl/container:inlined_vector",
        "@com_google_absl//absl/strings",
        "@com_google_absl//absl/strings:str_format",
        "@com_google_absl//absl/time",
        "@com_google_protobuf//:protobuf",
    ],
)

cc_library(
    name = "test_helpers",
    srcs = ["test/gtest/helpers.cpp"],
    hdrs = ["test/gtest/helpers.h"],
    data = ["test"],
    deps = [
        ":ir_frontend_midend_control_plane",
        "@com_google_googletest//:gtest",
    ],
)

cc_library(
    name = "p4c_backends_common_lib",
    srcs = glob(["backends/common/*.cpp"]),
    hdrs = glob(["backends/common/*.h"]),
    deps = [
        ":ir_frontend_midend_control_plane",
        ":lib",
    ],
)

cc_library(
    name = "p4c_bmv2_common_lib",
    srcs = glob(["backends/bmv2/common/*.cpp"]),
    hdrs = glob(["backends/bmv2/common/*.h"]),
    deps = [
        ":ir_frontend_midend_control_plane",
        ":lib",
        ":p4c_backends_common_lib",
    ],
)

cc_library(
    name = "p4c_bmv2_simple_lib",
    srcs = glob(
        ["backends/bmv2/simple_switch/*.cpp"],
        exclude = ["backends/bmv2/simple_switch/main.cpp"],
    ),
    hdrs = glob(["backends/bmv2/simple_switch/*.h"]),
    deps = [
        ":ir_frontend_midend_control_plane",
        ":lib",
        ":p4c_backends_common_lib",
        ":p4c_bmv2_common_lib",
    ],
)

genrule(
    name = "p4c_bmv2_version",
    srcs = ["backends/bmv2/simple_switch/version.h.cmake"],
    outs = ["backends/bmv2/simple_switch/version.h"],
    cmd = "sed 's|@P4C_VERSION@|0.0.0.0|g' $(SRCS) > $(OUTS)",
    visibility = ["//visibility:private"],
)

cc_binary(
    name = "p4c_bmv2",
    srcs = [
        "backends/bmv2/simple_switch/main.cpp",
        "backends/bmv2/simple_switch/version.h",
    ],
    data = [":p4include"],
    deps = [
        ":ir_frontend_midend_control_plane",
        ":lib",
        ":p4c_backends_common_lib",
        ":p4c_bmv2_common_lib",
        ":p4c_bmv2_simple_lib",
    ],
)

# dpdk backend
genrule(
    name = "dest_dir_dpdk",
    srcs = ["backends/dpdk/control-plane/proto/p4info.proto"],
    outs = ["backends/dpdk/p4/config/p4info.proto"],
    cmd = "mkdir -p p4/config/dpdk; cp $(SRCS) $(OUTS)",
    visibility = ["//visibility:private"],
)

proto_library(
    name = "p4info_dpdk_proto",
    srcs = ["backends/dpdk/p4/config/p4info.proto"],
    deps = [
        "@com_google_protobuf//:any_proto",
        "@com_google_protobuf//:descriptor_proto",
    ],
)

cc_proto_library(
    name = "p4info_dpdk_cc_proto",
    deps = [":p4info_dpdk_proto"],
)

cc_library(
    name = "p4c_dpdk_lib",
    srcs = glob(
        [
            "backends/dpdk/*.cpp",
            "backends/dpdk/control-plane/*.cpp",
        ],
        exclude = [
            "backends/dpdk/main.cpp",
            "backends/dpdk/spec.cpp",
            "backends/dpdk/printUtils.cpp",
            "backends/dpdk/dbprint-dpdk.cpp",
        ],
    ) + ["backends/bmv2/common/lower.cpp"],
    hdrs = glob([
        "backends/dpdk/*.h",
        "backends/dpdk/control-plane/*.h",
    ]) + ["backends/bmv2/common/lower.h"],
    deps = [
        ":ir_frontend_midend_control_plane",
        ":lib",
        ":p4info_dpdk_cc_proto",
    ],
)

genrule(
    name = "p4c_dpdk_version",
    srcs = ["backends/dpdk/version.h.cmake"],
    outs = ["backends/dpdk/version.h"],
    cmd = "sed 's|@P4C_VERSION@|0.0.0.0|g' $(SRCS) > $(OUTS)",
    visibility = ["//visibility:private"],
)

cc_binary(
    name = "p4c_dpdk",
    srcs = [
        "backends/dpdk/main.cpp",
        "backends/dpdk/version.h",
    ],
    data = [":p4include"],
    deps = [
        ":ir_frontend_midend_control_plane",
        ":lib",
        ":p4c_dpdk_lib",
    ],
)

# This builds the p4test backend.
cc_binary(
    name = "p4c_backend_p4test",
    srcs = [
        "backends/p4test/p4test.cpp",
    ],
    deps = [
        ":ir_frontend_midend_control_plane",
        ":lib",
        ":p4c_backend_p4test_lib",
    ],
)

genrule(
    name = "p4c_p4test_version",
    srcs = ["backends/p4test/version.h.cmake"],
    outs = ["backends/p4test/version.h"],
    cmd = "sed 's|@P4C_VERSION@|0.0.0.0|g' $(SRCS) > $(OUTS)",
    visibility = ["//visibility:private"],
)

cc_library(
    name = "p4c_backend_p4test_lib",
    srcs = [
        "backends/p4test/midend.cpp",
    ],
    hdrs = [
        "backends/p4test/midend.h",
        "backends/p4test/version.h",
    ],
    deps = [
        ":ir_frontend_midend_control_plane",
        ":lib",
    ],
)

# These rules build the graphs backend
cc_binary(
    name = "p4c_graphs",
    srcs = [
        "backends/graphs/p4c-graphs.cpp",
    ],
    data = [":p4include"],
    deps = [
        ":config_h",
        ":ir_frontend_midend_control_plane",
        ":lib",
        ":p4c_backend_graphs_lib",
    ],
)

genrule(
    name = "p4c_graphs_version",
    srcs = ["backends/graphs/version.h.cmake"],
    outs = ["backends/graphs/version.h"],
    cmd = "sed 's|@P4C_VERSION@|0.0.0.0|g' $(SRCS) > $(OUTS)",
    visibility = ["//visibility:private"],
)

cc_library(
    name = "p4c_backend_graphs_lib",
    srcs = [
        "backends/graphs/controls.cpp",
        "backends/graphs/graph_visitor.cpp",
        "backends/graphs/graphs.cpp",
        "backends/graphs/p4c-graphs.cpp",
        "backends/graphs/parsers.cpp",
    ],
    hdrs = [
        "backends/graphs/controls.h",
        "backends/graphs/graph_visitor.h",
        "backends/graphs/graphs.h",
        "backends/graphs/parsers.h",
        "backends/graphs/version.h",
    ],
    defines = [
        # Disable ADL for boost, otherwise the method resolution for
        # `ordered_map` and `graph` clash on method `boost::get`
        "BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP",
    ],
    deps = [
        ":config_h",
        ":ir_frontend_midend_control_plane",
        ":lib",
        "@boost//:graph",
    ],
)
